#ifndef __BLOCKQUEUE_HPP__
#define __BLOCKQUEUE_HPP__
#include <queue>
#include <pthread.h>

using namespace std;

// 阻塞队列（交易场所，属于共享资源应该保护起来）
template <typename T>
class BlockQueue
{
private:
    bool isFull()
    {
        return _bq.size() == _maxcap;
    }

    bool isEmpty()
    {
        return _bq.empty();
    }

public:
    BlockQueue(int maxcap) : _maxcap(maxcap)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_producer_cond, nullptr);
        pthread_cond_init(&_consumer_cond, nullptr);
    }

    void push(T &data) // 生产者调用
    {
        // 第一步：加锁
        pthread_mutex_lock(&_mutex);

        // 第二步：生产数据
        if (isFull())
        {
            // 阻塞队列满，条件变量下等待
            // pthread_cond_wait做了什么工作呢？
            // 1、释放当前线程持有的锁，因为它不能拿着锁去等待，拿着锁会导致其他线程无法竞争到锁
            // 2、当等待的线程被唤醒的时候，从这里接着运行
            // 3、唤醒之后要重新竞争锁，竞争成功方可返回
            pthread_cond_wait(&_producer_cond, &_mutex);
        }
        _bq.push(data);

        // 第三步：唤醒消费者+释放锁
        pthread_cond_signal(&_consumer_cond);
        pthread_mutex_unlock(&_mutex);
    }

    void pop(T &data) // 消费者调用
    {
        // 第一步：加锁
        pthread_mutex_lock(&_mutex);

        // 第二步：消费数据
        if (isEmpty())
        {
            // 阻塞队列为空，条件变量下等待
            pthread_cond_wait(&_consumer_cond, &_mutex);
        }
        data = _bq.front();
        _bq.pop();

        // 第三步：唤醒生产者+释放锁
        pthread_cond_signal(&_producer_cond);
        pthread_mutex_unlock(&_mutex);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_producer_cond);
        pthread_cond_destroy(&_consumer_cond);
    }

private:
    queue<T> _bq;
    int _maxcap; // 队列的容量上限

    pthread_mutex_t _mutex;        // 保护阻塞队列的锁
    pthread_cond_t _producer_cond; // 生产者条件变量
    pthread_cond_t _consumer_cond; // 消费者条件变量
};

#endif
